package com.liuyi.mybatisplus.plus.condition;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liuyi.tools.kits.EnumsKits;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @ClassName : QueryWrapperAnalysis  //类名
 * @Description : 查询解析  //描述
 * @Author : Mr.Fmy  //作者
 * @Date: 2020-07-10 14:08  //时间
 */
@Slf4j
public class QueryWrapperAnalysis {

    /**
     * 如参是否驼峰命名
     */
    public static Boolean UNDERLINE_CASE = true;

    public static String UNDERSCORE_CONVERSION = "-";

    private static ThreadLocal<Map<String, Class>> THREAD_MAP = new ThreadLocal<>();

    /**
     * @param table 如果是主表，如果是单表查询，并且是主表，需要用this作为key
     */
    public static <T> QueryWrapper<T> analysis(final QueryWrapper<T> q, QueryCondition condition, Map<String, Class> table) {
        LinkedHashMap<String, Object> query = condition.getQuery();
        Map<String, String> sort = condition.getSort();
        //解析条件
        try {
            if (CollUtil.isNotEmpty(table)) {
                THREAD_MAP.set(table);
            }
            queryWrapper(query, q);
        } finally {
            THREAD_MAP.remove();
        }
        if (CollUtil.isNotEmpty(sort)) {
            sort.forEach((k, v) -> sort(q, k, v));
        }
        if (StrUtil.isNotBlank(condition.getSelect())) {
            q.select(condition.getSelect());
        }
        return q;
    }

    private static String select(String select) {
        //过滤一下不允许执行的函数
        //数据库版本泄露函数
        return select.replaceAll("version()", "");
    }

    private static <T> void sort(final QueryWrapper<T> q, String k, String v) {
        if (StrUtil.isNotBlank(v)) {
            String[] c = clounm(k);
            if (StrUtil.isNotBlank(c[1])) {
                q.orderBy(true, "asc".equals(v), c[1]);
            }
        }
    }

    private static Object wrapperValueEnum(String clounm, Object v) {
        Map<String, Class> stringClassMap = THREAD_MAP.get();
        if (CollUtil.isEmpty(stringClassMap)) {
            return v;
        }
        String s = clounmTable(clounm, false);
        String key = UNDERLINE_CASE ? StrUtil.toCamelCase(clounm) : clounm;
        if (StrUtil.isBlank(s)) {
            //取this
            if (stringClassMap.containsKey("this")) {
                Class<?> aThis = stringClassMap.get("this");
                return wrapperValueEnum(key, v, "", aThis);
            }
        } else {
            //如果前辍不为空，就取前辍
            if (stringClassMap.containsKey(s)) {
                Class<?> table = stringClassMap.get(s);
                //这里加个.用于比对
                return wrapperValueEnum(key, v, s + ".", table);
            }
        }
        return v;
    }

    private static Object wrapperValueEnum(String clounm, Object v, String pre, Class tableClass) {
        if (v instanceof ArrayList) {
            for (int i = 0; i < ((ArrayList<Object>) v).size(); i++) {
                Object o = EnumsKits.wraEnumValue(clounm, ((ArrayList<?>) v).get(i), pre, tableClass);
                ((ArrayList<Object>) v).set(i,o);
            }
            return v;
        } else {
            return EnumsKits.wraEnumValue(clounm, v, pre, tableClass);
        }
    }

    private static <T> void queryWrapper(Map<String, Object> query, final QueryWrapper<T> q) {
        //拼接查询条件
        if (CollUtil.isNotEmpty(query)) {
            query.forEach((k, v) -> {
                String[] c = clounm(k);
                if (c.length >= 1 && c.length <= 3) {
                    ConditionKeyword keyword = EnumUtil.fromString(ConditionKeyword.class, c[0]);
                    //解析枚举
                    v = wrapperValueEnum(c[1], v);
                    query(q, c[1], c.length == 3 ? c[2] : null, v, keyword);
                }
            });
        }
    }

    private static <T> void query(final QueryWrapper<T> q, String k, String logic, Object v, ConditionKeyword keyword) {
        boolean isCondition = ObjectUtil.isNotEmpty(v);
        boolean isK = StrUtil.isNotBlank(k);
        if (keyword.equals(ConditionKeyword.NESTED) || isK) {
            switch (keyword) {
                case EQ:
                    q.eq(isCondition, k, v);
                    break;
                case NE:
                    q.ne(isCondition, k, v);
                    break;
                case LIKE:
                    q.like(isCondition, k, v);
                    break;
                case LIKELEFT:
                    q.likeLeft(isCondition, k, v);
                    break;
                case LIKERIGHT:
                    q.likeRight(isCondition, k, v);
                    break;
                case GT:
                    q.gt(isCondition, k, v);
                    break;
                case GTE:
                    q.ge(isCondition, k, v);
                    break;
                case LT:
                    q.lt(isCondition, k, v);
                    break;
                case LTE:
                    q.le(isCondition, k, v);
                    break;
                case BETWEEN:
                    if (isCondition) {
                        Object[] vs = v.toString().split(",");
                        if (vs.length == 2) {
                            q.between(k, vs[0], vs[1]);
                        }
                    }
                    break;
                case IN:
                    if (v instanceof List) {
                        q.in(isCondition, k, ((List) v).toArray());
                    } else {
                        String[] split = v.toString().split(",");
                        q.in(isCondition, k, (Object[]) split);
                    }
                    break;
                case NOTIN:
                    q.notIn(isCondition, k, isCondition ? v.toString().split(",") : null);
                    break;
                case ISNULL:
                    q.isNull(k);
                    break;
                case NOTNULL:
                    q.isNotNull(k);
                    break;
                case NESTED:
                    if (isCondition && v instanceof List) {
                        final List arrays = (List) v;
                        q.nested(nq -> arrays.forEach(item -> {
                            if (item instanceof Map) {
                                queryWrapper((Map<String, Object>) item, nq);
                            }
                        }));
                    }
                    break;
            }
            if (StrUtil.isNotBlank(logic)) {
                switch (logic) {
                    case "OR":
                        q.or();
                }
            }
        }
    }

    /**
     * 解析列
     *
     * @param k k值
     * @return [0] 逻辑 [1] 列 [2] OR或不存在
     */
    private static String[] clounm(String k) {
        String[] c = k.split("_");
        if (c.length >= 2) {
            if (c[1].contains(UNDERSCORE_CONVERSION)) {
                //转下换线
                c[1] = StrUtil.toUnderlineCase(c[1].replaceAll(UNDERSCORE_CONVERSION, "_"));
            }
            if (UNDERLINE_CASE) {
                c[1] = StrUtil.toUnderlineCase(c[1]);
            }
        }
        return c;
    }

    private static String clounmTable(String clounm) {
        return clounmTable(clounm, true);
    }

    /**
     * 获取列名的表明，例如 a.name 获取 a 或者 a.
     *
     * @param clounm 列名，例如 name  a.name等
     * @param dot    是否包含. false 获取a true 获取 a.
     */
    private static String clounmTable(String clounm, boolean dot) {
        int i = clounm.indexOf(".");
        if (i == -1) {
            return null;
        }
        return clounm.substring(0, dot ? i + 1 : i);
    }

    public enum ConditionKeyword {
        EQ,
        NE,
        /**
         * 影响查询效率，暂时不用
         */
        IN,
        /**
         * 影响查询效率，暂时不用
         */
        NOTIN,
        LIKE,
        LIKELEFT,
        LIKERIGHT,
        /**
         * 大于
         */
        GT,
        /**
         * 小于
         */
        LT,
        /**
         * 大于等于
         */
        GTE,
        /**
         * 小于等于
         */
        LTE,
        /**
         * 范围
         */
        BETWEEN,
        ISNULL,
        NOTNULL,
        /**
         * 新开条件
         */
        NESTED,
        /**
         * 排序、这个就是排序使用的、其实没啥用，就是一种规范
         */
        ORDER;
    }
}
